/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package cbsnrelayparte;

import java.io.*;
import java.util.*;

/**
 *
 * @author Craig Butler (5668670)
 * @author Stephen Norman (616000X)
 * @version 1.0 22/05/2008
 */
final class ReaderWriter implements Runnable
{
    // The reader to read data from
    private DataInputStream _reader;
    
    // The writer to write data to
    private DataOutputStream _writer;
    
    // The array to store the data into
    byte[] data;
    
    // Read / Write Strings
    String _read;
    String _write;
    
    // Buffer to store packets sent out of order
    byte[] packetOutOfOrderData;
    boolean packetOutOfOrder = false;
    
    /**
     * Constructor for the ReaderWriter class
     * 
     * @param reader The reader to read
     * @param writer The writer to write
     */
    ReaderWriter(DataInputStream reader, DataOutputStream writer, String read, String write)
    {
        // Take the passed in reader/writer and set them to
        // the private variables
        _reader = reader;
        _writer = writer;
        
        // The strings
        _read = read;
        _write = write;        
    }
    
    /**
     * Executed when the thread is started
     */
    public void run()
    {
        startReadingAndWriting();
    }
    
    /**
     * Start reading from reader and writing to the writer
     */
    private void startReadingAndWriting()
    {   
        try
        {
            // Loop forever
            while (true)
            {
                // Create a new array to store the data
                data = new byte[1460];

                // We are now waiting for some data
                System.out.println(Util.getTime() + "Waiting for data from " + _read + "...");
                                
                // Read all l460
                _reader.readFully(data);
                
                // Wait 10 msecs
                Thread.sleep(20);
                
                // We have now received a packet
                System.out.println(Util.getTime() + "Data received from " + _read);
                
                // Should we drop the packet?
                if (dropPacket() == true)
                {
                    continue;
                }
                
                // Should we place the packet out of order?
                if (outOfOrderPacket() == true && packetOutOfOrder == false)
                {
                    // Store the data for transmission next time
                    packetOutOfOrderData = data;
                    
                    // We have a packet out of order
                    packetOutOfOrder = true;                 
                    
                    // Skip the rest of this iteration
                    continue;
                }
                
                // We need to decide if we are going to corrupt the packet or not
                data = corruption(data);
                
                // We are about to write the data out to the writer
                System.out.println(Util.getTime() + "Writing data to " + _write + "...");
                
                // Write out the bytes
                _writer.write(data);
                
                // Check to see if we should write a packet out of order
                if (packetOutOfOrder == true)
                {
                    // Wait about 15 msecs
                    Thread.sleep(15);
                    
                    // Tell the user what is going on
                    System.out.println(Util.getTime() + "Forwarding out of order packet to " + _write + "...");
                    
                    // Forward the packet
                    _writer.write(packetOutOfOrderData);
                    
                    // We are no longer out of order
                    packetOutOfOrder = false;
                }
                
                // Leave a blank line
                System.out.println();
            }
        }
        catch (Exception ex)
        {
            System.out.println("Error: " + ex.getMessage());
        }
    }
    
    /**
     * Randomly decides to corrupt a packet
     * 
     * @param data The data to possibly be corrupted
     * @return The data that may have been corrupted
     */
    private byte[] corruption(byte[] data)
    {
        // Generate a random number to decide if we will corrupt the packet or not
        Random rnd = new Random( System.currentTimeMillis() );
        int num = rnd.nextInt(20);
        
        // If the number is greater than 15 we will corrupt the packet
        if (num > 15)
        {
            // Say that we are corrupting the packet
            System.out.println(Util.getTime() + "Packet from " + _read + " has been corrupted");
            
            // Let's corrupt the packet
            byte[] corruption = "CORRUPT".getBytes();

            // Add the corruption bits to the packet
            for (int i = 0; i < corruption.length; i++)
            {
                data[i + 5] = corruption[i];
            }
        }
        
        // Return the data back to the function
        return data;
    }
    
    /**
     * Randomly decides to drop a packet
     * 
     * @return Should this packet be dropped
     */
    private boolean dropPacket()
    {
        // Generate a random number to decide if we will drop the packet or not
        Random rnd = new Random( System.currentTimeMillis() );
        int num = rnd.nextInt(30);
        
        // If the number is greater than 25 we will drop the packet
        if (num > 25)
        {
            // Say that we are corrupting the packet
            System.out.println(Util.getTime() + "Packet from " + _read + " will be dropped");

            // The packet will be dropped
            return true;
        }
        
        // We will not drop the packet
        return false;
    }
    
    /**
     * Randomly decides to place a packet out of order
     * 
     * @return Has this packet been placed out of order
     */
    private boolean outOfOrderPacket()
    {
        // Generate a random number to decide if we will drop the packet or not
        Random rnd = new Random( System.currentTimeMillis() );
        int num = rnd.nextInt(30);
        
        // If the number is greater than 25 we will drop the packet
        if (num > 25)
        {
            // Say that we are corrupting the packet
            System.out.println(Util.getTime() + "Packet from " + _read + " will be placed out of order.");
            
            // The packet will be dropped
            return true;
        }
        
        // We will not drop the packet
        return false;
    }
}
